import { NgModule, Optional, SkipSelf, NgModuleRef, NgZone } from '@angular/core';
import { RouteReuseStrategy } from '@angular/router';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NZ_CONFIG, NzConfig, NZ_ICONS } from 'ng-zorro-antd';
import { NzMessageModule } from 'ng-zorro-antd/message';
import { NzModalModule } from 'ng-zorro-antd/modal';
import {
  CloseCircleFill, SearchOutline, CaretLeftOutline, LeftOutline, CloseOutline,
  CaretRightOutline, LoadingOutline, RightOutline
} from '@ant-design/icons-angular/icons';

import { InterceptorService } from './http/interceptor.service';
import {
  STORE_PREFIX, STORE_VERSION, DICT_BASE_STORAGE_NAME, ApplicationEnv, ROUTE_REUSE, MATH_SCRIPT_PATHS, SESSION_EXPIRY_TIME,
  NAV_TAB_MAX_CACHE, NAV_TAB_DISPLAY_MAX_COUNT, NAV_TAB_DISABLE_LOAD, VIEW_HEADER_FIXED, SESSION_CHECK_WHITELIST_PAGES, DISABLED_INIT_LOAD_STORE_KEY, LOGGER_REWRITE_NATIVE_METHOD
} from './keys';
import { ReuseStrategyService } from './service/reuse-strategy.service';
import { LoggerService } from './service/logger.service';
import { Steve } from './steve';

const icons = [CloseCircleFill, SearchOutline, CaretLeftOutline, LeftOutline, CloseOutline, CaretRightOutline, LoadingOutline, RightOutline];
const ngZorroConfig: NzConfig = {
  message: {// message组件全局配置
    nzDuration: 5000,
    nzMaxStack: 1,
    nzPauseOnHover: true,
    nzAnimate: true
  },
  modal: {// modal组件全局配置
    nzMask: false
  }
};

const normalProviders: any[] = [
  { provide: STORE_PREFIX, useValue: 'bz-' }, // 离线缓存key的前缀
  { provide: STORE_VERSION, useValue: 2 }, // 离线数据的版本，用于强制清除数据
  { provide: DICT_BASE_STORAGE_NAME, useValue: 'base' },
  { provide: HTTP_INTERCEPTORS, useClass: InterceptorService, multi: true },
  { provide: NZ_CONFIG, useValue: ngZorroConfig },
  { provide: NZ_ICONS, useValue: icons },
  { provide: SESSION_EXPIRY_TIME, useValue: 1000000 },
  { provide: NAV_TAB_MAX_CACHE, useValue: 20 },
  { provide: NAV_TAB_DISPLAY_MAX_COUNT, useValue: 50 },
  { provide: NAV_TAB_DISABLE_LOAD, useValue: false },
  { provide: VIEW_HEADER_FIXED, useValue: true },
  { provide: LOGGER_REWRITE_NATIVE_METHOD, useValue: false },
  // 为了提高启动速度，禁止部分数据在初始化阶段加载，延迟到使用时加载
  { provide: DISABLED_INIT_LOAD_STORE_KEY, multi: true, useValue: ['Acl', 'TempData'] },
  {
    provide: SESSION_CHECK_WHITELIST_PAGES,
    useValue: [
      '^/([\\?#;][^/]*|)$',
      '^/entry',
      '^/extension/system'
    ]
  },
  { provide: MATH_SCRIPT_PATHS, useValue: ['./assets/lib/mathjs/mathjs.min.js'] },
];

@NgModule({
  imports: [
    HttpClientModule,
    NzMessageModule,
    NzModalModule,
  ],
  providers: [
    ...normalProviders,
    { provide: RouteReuseStrategy, useClass: ReuseStrategyService },
    { provide: ROUTE_REUSE, useValue: true },
  ]
})
export class CoreModule {
  private _env_instance = new ApplicationEnv();

  constructor(
    @Optional() @SkipSelf() parentModule: CoreModule,
    moduleRef: NgModuleRef<CoreModule>,
    private ngZone: NgZone,
    private loggerService: LoggerService
  ) {
    if (parentModule) {
      this.loggerService.error(new Error(
        'CoreModule is already loaded. Import it in the AppModule only'), 'CoreModule');
    }

    (<any>window)._moduleRef = moduleRef;

    const env = moduleRef.injector.get(ApplicationEnv);
    this.validateEnv(env);
    // 监听模块级路由DOM变更，提升渲染速度
    if (navigator.userAgent.indexOf('Firefox') > 0) {
      setTimeout(() => {
        const viewContainer = document.getElementsByClassName('view-container')[0];
        if (viewContainer) {
          new MutationObserver((mutationsList, observer) => {
            for (const mutation of mutationsList) {
              if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                viewContainer.dispatchEvent(new Event('click'));
              }
            }
          }).observe(viewContainer, {
            childList: true
          });
        }
      }, 3000);
    }
  }

  private validateEnv(env: any) {
    Object.keys(this._env_instance).forEach((key) => {
      if (!env.hasOwnProperty(key)) {
        this.ngZone.runOutsideAngular(() => {
          setTimeout(() => {
            this.loggerService.error(new Error(`environment must have '${key}' property`), 'CoreModule');
          }, 1000);
        });
      }
    });
  }

}
